<!DOCTYPE html>



  


<html class="theme-next muse use-motion" lang>
<head><meta name="generator" content="Hexo 3.9.0">
  <meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta name="theme-color" content="#222">









<meta http-equiv="Cache-Control" content="no-transform">
<meta http-equiv="Cache-Control" content="no-siteapp">
















  
  
  <link href="/blog/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css">







<link href="/blog/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css">

<link href="/blog/css/main.css?v=5.1.4" rel="stylesheet" type="text/css">


  <link rel="apple-touch-icon" sizes="180x180" href="/blog/images/apple-touch-icon-next.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="32x32" href="/blog/images/favicon-32x32-next.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="16x16" href="/blog/images/favicon-16x16-next.png?v=5.1.4">


  <link rel="mask-icon" href="/blog/images/logo.svg?v=5.1.4" color="#222">





  <meta name="keywords" content="react-router,">





  <link rel="alternate" href="/blog/atom.xml" title="编程那点事" type="application/atom+xml">






<meta name="description" content="本文的意图并不是重复 React Router 4 已经写得很好的文档。我将介绍最常见的 API，但真正的重点是我发现的成功模式和策略。 对于本文，以下是一些你需要熟悉的 JavaScript 概念:  React （无状态）函数组件 ES2015 箭头函数 以及它们的“隐式返回” ES2015 解构 ES2015 模板字符串如果你喜欢跳转到演示区的话，请点这里：  查看演示 新的 API 和新的">
<meta name="keywords" content="react-router">
<meta property="og:type" content="article">
<meta property="og:title" content="关于 React Router 4 的一切">
<meta property="og:url" content="https://lhweb.gitee.io/blog/2018/09/24/关于-React-Router-4-的一切/index.html">
<meta property="og:site_name" content="编程那点事">
<meta property="og:description" content="本文的意图并不是重复 React Router 4 已经写得很好的文档。我将介绍最常见的 API，但真正的重点是我发现的成功模式和策略。 对于本文，以下是一些你需要熟悉的 JavaScript 概念:  React （无状态）函数组件 ES2015 箭头函数 以及它们的“隐式返回” ES2015 解构 ES2015 模板字符串如果你喜欢跳转到演示区的话，请点这里：  查看演示 新的 API 和新的">
<meta property="og:locale" content="default">
<meta property="og:updated_time" content="2019-10-03T05:53:45.942Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="关于 React Router 4 的一切">
<meta name="twitter:description" content="本文的意图并不是重复 React Router 4 已经写得很好的文档。我将介绍最常见的 API，但真正的重点是我发现的成功模式和策略。 对于本文，以下是一些你需要熟悉的 JavaScript 概念:  React （无状态）函数组件 ES2015 箭头函数 以及它们的“隐式返回” ES2015 解构 ES2015 模板字符串如果你喜欢跳转到演示区的话，请点这里：  查看演示 新的 API 和新的">



<script type="text/javascript" id="hexo.configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '/blog/',
    scheme: 'Muse',
    version: '5.1.4',
    sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false},
    fancybox: true,
    tabs: true,
    motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},
    duoshuo: {
      userId: '0',
      author: 'Author'
    },
    algolia: {
      applicationID: '',
      apiKey: '',
      indexName: '',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    }
  };
</script>



  <link rel="canonical" href="https://lhweb.gitee.io/blog/2018/09/24/关于-React-Router-4-的一切/">





  <title>关于 React Router 4 的一切 | 编程那点事</title>
  








</head>

<body itemscope itemtype="http://schema.org/WebPage" lang="default">

  
  
    
  

  <div class="container sidebar-position-left page-post-detail">
    <div class="headband"></div>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-wrapper">
  <div class="site-meta ">
    

    <div class="custom-logo-site-title">
      <a href="/blog/" class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">编程那点事</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>
      
        <p class="site-subtitle"></p>
      
  </div>

  <div class="site-nav-toggle">
    <button>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
    </button>
  </div>
</div>

<nav class="site-nav">
  

  
    <ul id="menu" class="menu">
      
        
        <li class="menu-item menu-item-home">
          <a href="/blog/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-home"></i> <br>
            
            Home
          </a>
        </li>
      
        
        <li class="menu-item menu-item-archives">
          <a href="/blog/archives/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-archive"></i> <br>
            
            Archives
          </a>
        </li>
      

      
    </ul>
  

  
</nav>



 </div>
    </header>

    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          <div id="content" class="content">
            

  <div id="posts" class="posts-expand">
    

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="https://lhweb.gitee.io/blog/blog/2018/09/24/关于-React-Router-4-的一切/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="lhweb">
      <meta itemprop="description" content>
      <meta itemprop="image" content="/blog/images/avatar.gif">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="编程那点事">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">关于 React Router 4 的一切</h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">Posted on</span>
              
              <time title="Post created" itemprop="dateCreated datePublished" datetime="2018-09-24T11:23:52+08:00">
                2018-09-24
              </time>
            

            

            
          </span>

          
            <span class="post-category">
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">In</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/react/" itemprop="url" rel="index">
                    <span itemprop="name">react</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        <p>本文的意图并不是重复 React Router 4 已经写得很好的文档。我将介绍最常见的 API，但真正的重点是我发现的成功模式和策略。</p>
<p>对于本文，以下是一些你需要熟悉的 JavaScript 概念:</p>
<ul>
<li>React （无状态）函数组件</li>
<li>ES2015 箭头函数 以及它们的“隐式返回”</li>
<li>ES2015 解构</li>
<li>ES2015 模板字符串<br>如果你喜欢跳转到演示区的话，请点这里：</li>
</ul>
<p><a href="https://codepen.io/bradwestfall/project/editor/XWNWge?preview_height=50&amp;open_file=src/app.js" target="_blank" rel="noopener">查看演示</a></p>
<h1 id="新的-API-和新的思维模式"><a href="#新的-API-和新的思维模式" class="headerlink" title="新的 API 和新的思维模式"></a>新的 API 和新的思维模式</h1><p>React Router 的早期版本将路由规则集中在一个位置，使它们与布局组件分离。当然，路由可以被划分成多个文件，但从概念上讲，路由是一个单元，基本上是一个美化的配置文件。</p>
<p>或许了解 v4 不同之处的最好方法是用每个版本编写一个简单的两页应用程序并进行比较。示例应用程序只有两个路由，对应首页和用户页面。</p>
<p><strong>这里是 v3 的：</strong><br><figure class="highlight dust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="xml">import </span><span class="template-variable">&#123; Router, Route, IndexRoute &#125;</span><span class="xml"> from 'react-router'</span></span><br><span class="line"><span class="xml"> </span></span><br><span class="line"><span class="xml">const PrimaryLayout = props =&gt; (</span></span><br><span class="line"><span class="xml">  <span class="tag">&lt;<span class="name">div</span> <span class="attr">className</span>=<span class="string">"primary-layout"</span>&gt;</span></span></span><br><span class="line"><span class="xml">    <span class="tag">&lt;<span class="name">header</span>&gt;</span></span></span><br><span class="line"><span class="xml">      Our React Router 3 App</span></span><br><span class="line"><span class="xml">    <span class="tag">&lt;/<span class="name">header</span>&gt;</span></span></span><br><span class="line"><span class="xml">    <span class="tag">&lt;<span class="name">main</span>&gt;</span></span></span><br><span class="line"><span class="xml">      </span><span class="template-variable">&#123;props.children&#125;</span><span class="xml"></span></span><br><span class="line"><span class="xml">    <span class="tag">&lt;/<span class="name">main</span>&gt;</span></span></span><br><span class="line"><span class="xml">  <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line"><span class="xml">)</span></span><br><span class="line"><span class="xml"> </span></span><br><span class="line"><span class="xml">const HomePage =() =&gt; <span class="tag">&lt;<span class="name">div</span>&gt;</span>Home Page<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line"><span class="xml">const UsersPage = () =&gt; <span class="tag">&lt;<span class="name">div</span>&gt;</span>Users Page<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line"><span class="xml"> </span></span><br><span class="line"><span class="xml">const App = () =&gt; (</span></span><br><span class="line"><span class="xml">  <span class="tag">&lt;<span class="name">Router</span> <span class="attr">history</span>=</span></span><span class="template-variable">&#123;browserHistory&#125;</span><span class="xml"><span class="tag">&gt;</span></span></span><br><span class="line"><span class="xml">    <span class="tag">&lt;<span class="name">Route</span> <span class="attr">path</span>=<span class="string">"/"</span> <span class="attr">component</span>=</span></span><span class="template-variable">&#123;PrimaryLayout&#125;</span><span class="xml"><span class="tag">&gt;</span></span></span><br><span class="line"><span class="xml">      <span class="tag">&lt;<span class="name">IndexRoute</span> <span class="attr">component</span>=</span></span><span class="template-variable">&#123;HomePage&#125;</span><span class="xml"><span class="tag"> /&gt;</span></span></span><br><span class="line"><span class="xml">      <span class="tag">&lt;<span class="name">Route</span> <span class="attr">path</span>=<span class="string">"/users"</span> <span class="attr">component</span>=</span></span><span class="template-variable">&#123;UsersPage&#125;</span><span class="xml"><span class="tag"> /&gt;</span></span></span><br><span class="line"><span class="xml">    <span class="tag">&lt;/<span class="name">Route</span>&gt;</span></span></span><br><span class="line"><span class="xml">  <span class="tag">&lt;/<span class="name">Router</span>&gt;</span></span></span><br><span class="line"><span class="xml">)</span></span><br><span class="line"><span class="xml"> </span></span><br><span class="line"><span class="xml">render(<span class="tag">&lt;<span class="name">App</span> /&gt;</span>, document.getElementById('root'))</span></span><br></pre></td></tr></table></figure></p>
<p>以下是 v3 中的一些核心思想，但在 v4 中是不正确的:</p>
<p>路由集中在一个地方。<br>布局和页面嵌套是通过 &lt;Route> 组件的嵌套而来的。<br>布局和页面组件是完全纯粹的，它们是路由的一部分。<br>React Router 4 不再主张集中式路由了。相反，路由规则位于布局和 UI 本身之间。例如，以下是<strong> v4 中的相同的应用程序：</strong><br><figure class="highlight dust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="xml">import </span><span class="template-variable">&#123; BrowserRouter, Route &#125;</span><span class="xml"> from 'react-router-dom'</span></span><br><span class="line"><span class="xml"> </span></span><br><span class="line"><span class="xml">const PrimaryLayout = () =&gt; (</span></span><br><span class="line"><span class="xml">  <span class="tag">&lt;<span class="name">div</span> <span class="attr">className</span>=<span class="string">"primary-layout"</span>&gt;</span></span></span><br><span class="line"><span class="xml">    <span class="tag">&lt;<span class="name">header</span>&gt;</span></span></span><br><span class="line"><span class="xml">      Our React Router 4 App</span></span><br><span class="line"><span class="xml">    <span class="tag">&lt;/<span class="name">header</span>&gt;</span></span></span><br><span class="line"><span class="xml">    <span class="tag">&lt;<span class="name">main</span>&gt;</span></span></span><br><span class="line"><span class="xml">      <span class="tag">&lt;<span class="name">Route</span> <span class="attr">path</span>=<span class="string">"/"</span> <span class="attr">exact</span> <span class="attr">component</span>=</span></span><span class="template-variable">&#123;HomePage&#125;</span><span class="xml"><span class="tag"> /&gt;</span></span></span><br><span class="line"><span class="xml">      <span class="tag">&lt;<span class="name">Route</span> <span class="attr">path</span>=<span class="string">"/users"</span> <span class="attr">component</span>=</span></span><span class="template-variable">&#123;UsersPage&#125;</span><span class="xml"><span class="tag"> /&gt;</span></span></span><br><span class="line"><span class="xml">    <span class="tag">&lt;/<span class="name">main</span>&gt;</span></span></span><br><span class="line"><span class="xml">  <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line"><span class="xml">)</span></span><br><span class="line"><span class="xml"> </span></span><br><span class="line"><span class="xml">const HomePage =() =&gt; <span class="tag">&lt;<span class="name">div</span>&gt;</span>Home Page<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line"><span class="xml">const UsersPage = () =&gt; <span class="tag">&lt;<span class="name">div</span>&gt;</span>Users Page<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line"><span class="xml"> </span></span><br><span class="line"><span class="xml">const App = () =&gt; (</span></span><br><span class="line"><span class="xml">  <span class="tag">&lt;<span class="name">BrowserRouter</span>&gt;</span></span></span><br><span class="line"><span class="xml">    <span class="tag">&lt;<span class="name">PrimaryLayout</span> /&gt;</span></span></span><br><span class="line"><span class="xml">  <span class="tag">&lt;/<span class="name">BrowserRouter</span>&gt;</span></span></span><br><span class="line"><span class="xml">)</span></span><br><span class="line"><span class="xml"> </span></span><br><span class="line"><span class="xml">render(<span class="tag">&lt;<span class="name">App</span> /&gt;</span>, document.getElementById('root'))</span></span><br></pre></td></tr></table></figure></p>
<p>新的 API 概念：由于我们的应用程序是用于浏览器的，所以我们需要将它封装在来自 v4 的 BrowserRouter 中。还要注意的是我们现在从 react-router-dom 中导入它（这意味着我们安装的是 react-router-dom 而不是 react-router）。提示！现在叫做 react-router-dom 是因为还有一个 native 版本。</p>
<p>对于使用 React Router v4 构建的应用程序，首先看到的是“路由”似乎丢失了。在 v3 中，路由是我们的应用程序直接呈现给 DOM 的最巨大的东西。 现在，除了 &lt;BrowserRouter>外，我们首先抛给 DOM 的是我们的应用程序本身。</p>
<p>另一个在 v3 的例子中有而在 v4 中没有的是，使用 {props.children} 来嵌套组件。这是因为在 v4 中，&lt;Route> 组件在何处编写，如果路由匹配，子组件将在那里渲染。</p>
<h1 id="包容性路由"><a href="#包容性路由" class="headerlink" title="包容性路由"></a>包容性路由</h1><p>在前面的例子中，你可能已经注意到了 exact 这个属性。那么它是什么呢？V3 的路由规则是“排他性”的，这意味着只有一条路由将获胜。V4 的路由默认为“包含”的，这意味着多个 &lt;Route> 可以同时进行匹配和渲染。</p>
<p>在上一个例子中，我们试图根据路径渲染 HomePage 或者 UsersPage。如果从示例中删除了 exact 属性，那么在浏览器中访问 /users 时，HomePage 和 UsersPage 组件将同时被渲染。</p>
<p>要更好地了解匹配逻辑，请查看 path-to-regexp，这是 v4 现在正在使用的，以确定路由是否匹配 URL。</p>
<p>为了演示包容性路由是有帮助的，我们在标题中包含一个 UserMenu，但前提是我们在应用程序的用户部分：<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">const PrimaryLayout = () =&gt; (</span><br><span class="line">  <span class="tag">&lt;<span class="name">div</span> <span class="attr">className</span>=<span class="string">"primary-layout"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">header</span>&gt;</span></span><br><span class="line">      Our React Router 4 App</span><br><span class="line">      <span class="tag">&lt;<span class="name">Route</span> <span class="attr">path</span>=<span class="string">"/users"</span> <span class="attr">component</span>=<span class="string">&#123;UsersMenu&#125;</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">header</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">main</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">Route</span> <span class="attr">path</span>=<span class="string">"/"</span> <span class="attr">exact</span> <span class="attr">component</span>=<span class="string">&#123;HomePage&#125;</span> /&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">Route</span> <span class="attr">path</span>=<span class="string">"/users"</span> <span class="attr">component</span>=<span class="string">&#123;UsersPage&#125;</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">main</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">)</span><br></pre></td></tr></table></figure></p>
<p>现在，当用户访问 /users 时，两个组件都会渲染。类似这样的事情在 v3 中通过特定的匹配模式也是可行的，但它更复杂。得益于 v4 的包容性路由，现在能够很轻松地实现。</p>
<h1 id="排他性路由"><a href="#排他性路由" class="headerlink" title="排他性路由"></a>排他性路由</h1><p>如果你只需要在路由列表里匹配一个路由，则使用 &lt;Switch> 来启用排他路由：<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">const PrimaryLayout = () =&gt; (</span><br><span class="line">  <span class="tag">&lt;<span class="name">div</span> <span class="attr">className</span>=<span class="string">"primary-layout"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">PrimaryHeader</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">main</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">Switch</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">Route</span> <span class="attr">path</span>=<span class="string">"/"</span> <span class="attr">exact</span> <span class="attr">component</span>=<span class="string">&#123;HomePage&#125;</span> /&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">Route</span> <span class="attr">path</span>=<span class="string">"/users/add"</span> <span class="attr">component</span>=<span class="string">&#123;UserAddPage&#125;</span> /&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">Route</span> <span class="attr">path</span>=<span class="string">"/users"</span> <span class="attr">component</span>=<span class="string">&#123;UsersPage&#125;</span> /&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">Redirect</span> <span class="attr">to</span>=<span class="string">"/"</span> /&gt;</span></span><br><span class="line">      <span class="tag">&lt;/<span class="name">Switch</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">main</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">)</span><br></pre></td></tr></table></figure></p>
<p>在给定的 &lt;Switch> 路由中只有一条将渲染。在 HomePage 路由上，我们仍然需要 exact属性，尽管我们会先把它列出来。否则，当访问诸如 /users 或 /users/add 的路径时，主页路由也将匹配。事实上，战略布局是使用排他路由策略（因为它总是像传统路由那样使用）时的关键。请注意，我们在 /users 之前策略性地放置了 /users/add 的路由，以确保正确匹配。由于路径 /users/add 将匹配 /users 和 /users/add，所以最好先把 /users/add 放在前面。</p>
<p>当然，如果我们以某种方式使用 exact，我们可以把它们放在任何顺序上，但至少我们有选择。</p>
<p>如果遇到，&lt;Redirect> 组件将会始终执行浏览器重定向，但是当它位于 &lt;Switch> 语句中时，只有在其他路由不匹配的情况下，才会渲染重定向组件。想了解在非切换环境下如何使用 &lt;Redirect>，请参阅下面的授权路由。</p>
<p><strong>“默认路由”和“未找到”</strong></p>
<p>尽管在 v4 中已经没有 <indexroute> 了，但可以使用 <route exact> 来达到同样的效果。如果没有路由解析，则可以使用 <switch> 与 <redirect> 重定向到具有有效路径的默认页面（如同我对本示例中的 HomePage 所做的），甚至可以是一个“未找到页面”。</redirect></switch></route></indexroute></p>
<p><strong>嵌套布局</strong><br>你可能开始期待嵌套子布局，以及如何实现它们。我原本不认为我会纠结这个概念，但我确实纠结了。React Router v4 给了我们很多选择，这使它变得很强大。但是，选择意味着有选择不理想策略的自由。表面上看，嵌套布局很简单，但根据你的选择，可能会因为你组织路由的方式而遇到阻碍。</p>
<p>为了演示，假设我们想扩展我们的用户部分，所以我们会有一个“用户列表”页面和一个“用户详情”页面。我们也希望产品也有类似的页面。用户和产品都需要其个性化的子布局。例如，每个可能都有不同的导航选项卡。有几种方法可以解决这个问题，有的好，有的不好。第一种方法不是很好，但我想告诉你，这样你就不会掉入这个陷阱。第二种方法要好很多。</p>
<p>第一种方法，我们修改 PrimaryLayout，以适应用户和产品对应的列表及详情页面：<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">const PrimaryLayout = props =&gt; &#123;</span><br><span class="line">  return (</span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">className</span>=<span class="string">"primary-layout"</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">PrimaryHeader</span> /&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">main</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">Switch</span>&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">Route</span> <span class="attr">path</span>=<span class="string">"/"</span> <span class="attr">exact</span> <span class="attr">component</span>=<span class="string">&#123;HomePage&#125;</span> /&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">Route</span> <span class="attr">path</span>=<span class="string">"/users"</span> <span class="attr">exact</span> <span class="attr">component</span>=<span class="string">&#123;BrowseUsersPage&#125;</span> /&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">Route</span> <span class="attr">path</span>=<span class="string">"/users/:userId"</span> <span class="attr">component</span>=<span class="string">&#123;UserProfilePage&#125;</span> /&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">Route</span> <span class="attr">path</span>=<span class="string">"/products"</span> <span class="attr">exact</span> <span class="attr">component</span>=<span class="string">&#123;BrowseProductsPage&#125;</span> /&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">Route</span> <span class="attr">path</span>=<span class="string">"/products/:productId"</span> <span class="attr">component</span>=<span class="string">&#123;ProductProfilePage&#125;</span> /&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">Redirect</span> <span class="attr">to</span>=<span class="string">"/"</span> /&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">Switch</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;/<span class="name">main</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">  )</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>虽然这在技术上可行的，但仔细观察这两个用户页面就会发现问题：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> BrowseUsersPage = <span class="function"><span class="params">()</span> =&gt;</span> (</span><br><span class="line">  &lt;div className=<span class="string">"user-sub-layout"</span>&gt;</span><br><span class="line">    &lt;aside&gt;</span><br><span class="line">      &lt;UserNav /&gt;</span><br><span class="line">    &lt;<span class="regexp">/aside&gt;</span></span><br><span class="line"><span class="regexp">    &lt;div className="primary-content"&gt;</span></span><br><span class="line"><span class="regexp">      &lt;BrowseUserTable /</span>&gt;</span><br><span class="line">    &lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">  &lt;/</span>div&gt;</span><br><span class="line">)</span><br><span class="line"> </span><br><span class="line"><span class="keyword">const</span> UserProfilePage = <span class="function"><span class="params">props</span> =&gt;</span> (</span><br><span class="line">  &lt;div className=<span class="string">"user-sub-layout"</span>&gt;</span><br><span class="line">    &lt;aside&gt;</span><br><span class="line">      &lt;UserNav /&gt;</span><br><span class="line">    &lt;<span class="regexp">/aside&gt;</span></span><br><span class="line"><span class="regexp">    &lt;div className="primary-content"&gt;</span></span><br><span class="line"><span class="regexp">      &lt;UserProfile userId=&#123;props.match.params.userId&#125; /</span>&gt;</span><br><span class="line">    &lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">  &lt;/</span>div&gt;</span><br><span class="line">)</span><br></pre></td></tr></table></figure></p>
<p>新 API 概念：props.match 被赋到由 &lt;Route> 渲染的任何组件。你可以看到，userId 是由 props.match.params 提供的，了解更多请参阅 v4 文档。或者，如果任何组件需要访问 props.match，而这个组件没有由 &lt;Route> 直接渲染，那么我们可以使用 withRouter() 高阶组件。</p>
<p>每个用户页面不仅要渲染其各自的内容，而且还必须关注子布局本身（并且每个子布局都是重复的）。虽然这个例子很小，可能看起来微不足道，但重复的代码在一个真正的应用程序中可能是一个问题。更不用说，每次 BrowseUsersPage 或 UserProfilePage 被渲染时，它将创建一个新的 UserNav 实例，这意味着所有的生命周期方法都将重新开始。如果导航标签需要初始网络流量，这将导致不必要的请求 —— 这都是我们决定使用路由的方式造成的。</p>
<p>这里有另一种更好的方法：<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">const PrimaryLayout = props =&gt; &#123;</span><br><span class="line">  return (</span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">className</span>=<span class="string">"primary-layout"</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">PrimaryHeader</span> /&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">main</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">Switch</span>&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">Route</span> <span class="attr">path</span>=<span class="string">"/"</span> <span class="attr">exact</span> <span class="attr">component</span>=<span class="string">&#123;HomePage&#125;</span> /&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">Route</span> <span class="attr">path</span>=<span class="string">"/users"</span> <span class="attr">component</span>=<span class="string">&#123;UserSubLayout&#125;</span> /&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">Route</span> <span class="attr">path</span>=<span class="string">"/products"</span> <span class="attr">component</span>=<span class="string">&#123;ProductSubLayout&#125;</span> /&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">Redirect</span> <span class="attr">to</span>=<span class="string">"/"</span> /&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">Switch</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;/<span class="name">main</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">  )</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>与每个用户和产品页面相对应的四条路由不同，我们为每个部分的布局提供了两条路由。</p>
<p>请注意，上述示例没有使用 exact 属性，因为我们希望 /users 匹配任何以 /users 开头的路由，同样适用于产品。</p>
<p>通过这种策略，渲染其它路由将成为子布局的任务。UserSubLayout 可能如下所示：<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">const UserSubLayout = () =&gt; (</span><br><span class="line">  <span class="tag">&lt;<span class="name">div</span> <span class="attr">className</span>=<span class="string">"user-sub-layout"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">aside</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">UserNav</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">aside</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">className</span>=<span class="string">"primary-content"</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">Switch</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">Route</span> <span class="attr">path</span>=<span class="string">"/users"</span> <span class="attr">exact</span> <span class="attr">component</span>=<span class="string">&#123;BrowseUsersPage&#125;</span> /&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">Route</span> <span class="attr">path</span>=<span class="string">"/users/:userId"</span> <span class="attr">component</span>=<span class="string">&#123;UserProfilePage&#125;</span> /&gt;</span></span><br><span class="line">      <span class="tag">&lt;/<span class="name">Switch</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">)</span><br></pre></td></tr></table></figure></p>
<p>新策略中最明显的胜出在于所有用户页面之间的不重复布局。这是一个双赢，因为它不会像第一个示例那样具有相同生命周期的问题。</p>
<p>有一点需要注意的是，即使我们在布局结构中深入嵌套，路由仍然需要识别它们的完整路径才能匹配。为了节省重复输入（以防你决定将“用户”改为其他内容），请改用 props.match.path：<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">const UserSubLayout = props =&gt; (</span><br><span class="line">  <span class="tag">&lt;<span class="name">div</span> <span class="attr">className</span>=<span class="string">"user-sub-layout"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">aside</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">UserNav</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">aside</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">className</span>=<span class="string">"primary-content"</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">Switch</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">Route</span> <span class="attr">path</span>=<span class="string">&#123;props.match.path&#125;</span> <span class="attr">exact</span> <span class="attr">component</span>=<span class="string">&#123;BrowseUsersPage&#125;</span> /&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">Route</span> <span class="attr">path</span>=<span class="string">&#123;</span>`$&#123;<span class="attr">props.match.path</span>&#125;/<span class="attr">:userId</span>`&#125; <span class="attr">component</span>=<span class="string">&#123;UserProfilePage&#125;</span> /&gt;</span></span><br><span class="line">      <span class="tag">&lt;/<span class="name">Switch</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">)</span><br></pre></td></tr></table></figure></p>
<h1 id="匹配"><a href="#匹配" class="headerlink" title="匹配"></a>匹配</h1><p>到目前为止，props.match 对于知道详情页面渲染的 userId 以及如何编写我们的路由是很有用的。match 对象给我们提供了几个属性，包括 match.params、match.path、match.url 和其他几个。</p>
<p><strong>match.path vs match.url</strong><br>起初这两者之间的区别似乎并不清楚。控制台日志有时会显示相同的输出，这使得它们之间的差异更加模糊。例如，当浏览器路径为 /users 时，它们在控制台日志将输出相同的值：<br><figure class="highlight xquery"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">const UserSubLayout = (&#123; match &#125;) =&gt; &#123;</span><br><span class="line">  console.log(match.url)   // 输出：<span class="string">"/users"</span></span><br><span class="line">  console.log(match<span class="built_in">.path</span>)  // 输出：<span class="string">"/users"</span></span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    <span class="xml"><span class="tag">&lt;<span class="name">div</span> <span class="attr">className</span>=<span class="string">"user-sub-layout"</span>&gt;</span></span></span><br><span class="line"><span class="xml">      </span><span class="xml"><span class="tag">&lt;<span class="name">aside</span>&gt;</span></span></span><br><span class="line"><span class="xml">        <span class="tag">&lt;<span class="name">UserNav</span> /&gt;</span></span></span><br><span class="line"><span class="xml">      <span class="tag">&lt;/<span class="name">aside</span>&gt;</span></span><span class="xml"></span></span><br><span class="line"><span class="xml">      </span><span class="xml"><span class="tag">&lt;<span class="name">div</span> <span class="attr">className</span>=<span class="string">"primary-content"</span>&gt;</span></span></span><br><span class="line"><span class="xml">        </span><span class="xml"><span class="tag">&lt;<span class="name">Switch</span>&gt;</span></span></span><br><span class="line"><span class="xml">          <span class="tag">&lt;<span class="name">Route</span> <span class="attr">path</span>=</span></span><span class="xquery">&#123;match<span class="built_in">.path</span>&#125;</span><span class="xml"><span class="tag"> <span class="attr">exact</span> <span class="attr">component</span>=</span></span><span class="xquery">&#123;BrowseUsersPage&#125;</span><span class="xml"><span class="tag"> /&gt;</span></span></span><br><span class="line"><span class="xml">          <span class="tag">&lt;<span class="name">Route</span> <span class="attr">path</span>=</span></span><span class="xquery">&#123;`$&#123;match<span class="built_in">.path</span>&#125;</span><span class="xml"><span class="tag">/<span class="attr">:userId</span>`&#125; <span class="attr">component</span>=</span></span><span class="xquery">&#123;UserProfilePage&#125;</span><span class="xml"><span class="tag"> /&gt;</span></span></span><br><span class="line"><span class="xml">        <span class="tag">&lt;/<span class="name">Switch</span>&gt;</span></span><span class="xml"></span></span><br><span class="line"><span class="xml">      <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><span class="xml"></span></span><br><span class="line"><span class="xml">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line">  )</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p><strong>ES2015 概念： match 在组件函数的参数级别将被解构。</strong></p>
<p>虽然我们看不到差异，但 match.url 是浏览器 URL 中的实际路径，而 match.path 是为路由编写的路径。这就是为什么它们是一样的，至少到目前为止。但是，如果我们更进一步，在 UserProfilePage 中进行同样的控制台日志操作，并在浏览器中访问 /users/5，那么 match.url 将是 “/users/5” 而 match.path 将是 “/users/:userId”。</p>
<p><strong>选择哪一个？</strong><br>如果你要使用其中一个来帮助你构建路由路径，我建议你选择 match.path。使用 match.url 来构建路由路径最终会导致你不想看到的场景。下面是我遇到的一个情景。在一个像 UserProfilePage（当用户访问 /users/5 时渲染）的组件中，我渲染了如下这些子组件：<br><figure class="highlight dust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="xml">const UserComments = (</span><span class="template-variable">&#123; match &#125;</span><span class="xml">) =&gt; (</span></span><br><span class="line"><span class="xml">  <span class="tag">&lt;<span class="name">div</span>&gt;</span>UserId: </span><span class="template-variable">&#123;match.params.userId&#125;</span><span class="xml"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line"><span class="xml">)</span></span><br><span class="line"><span class="xml"> </span></span><br><span class="line"><span class="xml">const UserSettings = (</span><span class="template-variable">&#123; match &#125;</span><span class="xml">) =&gt; (</span></span><br><span class="line"><span class="xml">  <span class="tag">&lt;<span class="name">div</span>&gt;</span>UserId: </span><span class="template-variable">&#123;match.params.userId&#125;</span><span class="xml"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line"><span class="xml">)</span></span><br><span class="line"><span class="xml"> </span></span><br><span class="line"><span class="xml">const UserProfilePage = (</span><span class="template-variable">&#123; match &#125;</span><span class="xml">) =&gt; (</span></span><br><span class="line"><span class="xml">  <span class="tag">&lt;<span class="name">div</span>&gt;</span></span></span><br><span class="line"><span class="xml">    User Profile:</span></span><br><span class="line"><span class="xml">    <span class="tag">&lt;<span class="name">Route</span> <span class="attr">path</span>=</span></span><span class="template-variable">&#123;`$&#123;match.url&#125;</span><span class="xml"><span class="tag">/<span class="attr">comments</span>`&#125; <span class="attr">component</span>=</span></span><span class="template-variable">&#123;UserComments&#125;</span><span class="xml"><span class="tag"> /&gt;</span></span></span><br><span class="line"><span class="xml">    <span class="tag">&lt;<span class="name">Route</span> <span class="attr">path</span>=</span></span><span class="template-variable">&#123;`$&#123;match.path&#125;</span><span class="xml"><span class="tag">/<span class="attr">settings</span>`&#125; <span class="attr">component</span>=</span></span><span class="template-variable">&#123;UserSettings&#125;</span><span class="xml"><span class="tag"> /&gt;</span></span></span><br><span class="line"><span class="xml">  <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line"><span class="xml">)</span></span><br></pre></td></tr></table></figure></p>
<p>为了说明问题，我渲染了两个子组件，一个路由路径来自于 match.url，另一个来自 match.path。以下是在浏览器中访问这些页面时所发生的事情:</p>
<ul>
<li>访问 /users/5/comments 渲染 “UserId: undefined”。</li>
<li>访问 /users/5/settings 渲染 “UserId: 5”。<br>那么为什么 match.path 可以帮助我们构建路径 而 match.url 则不可以呢？答案就是这样一个事实：{${match.url}/comments} 基本上就像和硬编码的 {‘/users/5/comments’} 一样。这样做意味着后续组件将无法正确地填充 match.params，因为路径中没有参数，只有硬编码的 5。</li>
</ul>
<p>直到后来我看到文档的这一部分，才意识到它有多重要：</p>
<p><strong>match:</strong></p>
<ul>
<li>path - (string) 用于匹配路径模式。用于构建嵌套的 &lt;Route></li>
<li>url - (string) URL 匹配的部分。 用于构建嵌套的 &lt;Link></li>
</ul>
<p><strong>避免匹配冲突</strong><br>假设我们制作的应用程序是一个仪表版，所以我们希望能够通过访问 /users/add 和 /users/5/edit 来新增和编辑用户。但是在前面的例子中，users/:userId 已经指向了 UserProfilePage。那么这是否意味着带有users/:userId 的路由现在需要指向另一个子子布局来容纳编辑页面和详情页面？我不这么认为，因为编辑和详情页面共享相同的用户子布局，所以这个策略是可行的：<br><figure class="highlight dust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="xml">const UserSubLayout = (</span><span class="template-variable">&#123; match &#125;</span><span class="xml">) =&gt; (</span></span><br><span class="line"><span class="xml">  <span class="tag">&lt;<span class="name">div</span> <span class="attr">className</span>=<span class="string">"user-sub-layout"</span>&gt;</span></span></span><br><span class="line"><span class="xml">    <span class="tag">&lt;<span class="name">aside</span>&gt;</span></span></span><br><span class="line"><span class="xml">      <span class="tag">&lt;<span class="name">UserNav</span> /&gt;</span></span></span><br><span class="line"><span class="xml">    <span class="tag">&lt;/<span class="name">aside</span>&gt;</span></span></span><br><span class="line"><span class="xml">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">className</span>=<span class="string">"primary-content"</span>&gt;</span></span></span><br><span class="line"><span class="xml">      <span class="tag">&lt;<span class="name">Switch</span>&gt;</span></span></span><br><span class="line"><span class="xml">        <span class="tag">&lt;<span class="name">Route</span> <span class="attr">exact</span> <span class="attr">path</span>=</span></span><span class="template-variable">&#123;props.match.path&#125;</span><span class="xml"><span class="tag"> <span class="attr">component</span>=</span></span><span class="template-variable">&#123;BrowseUsersPage&#125;</span><span class="xml"><span class="tag"> /&gt;</span></span></span><br><span class="line"><span class="xml">        <span class="tag">&lt;<span class="name">Route</span> <span class="attr">path</span>=</span></span><span class="template-variable">&#123;`$&#123;match.path&#125;</span><span class="xml"><span class="tag">/<span class="attr">add</span>`&#125; <span class="attr">component</span>=</span></span><span class="template-variable">&#123;AddUserPage&#125;</span><span class="xml"><span class="tag"> /&gt;</span></span></span><br><span class="line"><span class="xml">        <span class="tag">&lt;<span class="name">Route</span> <span class="attr">path</span>=</span></span><span class="template-variable">&#123;`$&#123;match.path&#125;</span><span class="xml"><span class="tag">/<span class="attr">:userId</span>/<span class="attr">edit</span>`&#125; <span class="attr">component</span>=</span></span><span class="template-variable">&#123;EditUserPage&#125;</span><span class="xml"><span class="tag"> /&gt;</span></span></span><br><span class="line"><span class="xml">        <span class="tag">&lt;<span class="name">Route</span> <span class="attr">path</span>=</span></span><span class="template-variable">&#123;`$&#123;match.path&#125;</span><span class="xml"><span class="tag">/<span class="attr">:userId</span>`&#125; <span class="attr">component</span>=</span></span><span class="template-variable">&#123;UserProfilePage&#125;</span><span class="xml"><span class="tag"> /&gt;</span></span></span><br><span class="line"><span class="xml">      <span class="tag">&lt;/<span class="name">Switch</span>&gt;</span></span></span><br><span class="line"><span class="xml">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line"><span class="xml">  <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line"><span class="xml">)</span></span><br></pre></td></tr></table></figure></p>
<p>请注意，为了确保进行适当的匹配，新增和编辑路由需要战略性地放在详情路由之前。如果详情路径在前面，那么访问 /users/add 时将匹配详情（因为 “add” 将匹配 :userId）。</p>
<p>或者，如果我们这样创建路径 <code>${match.path}/:userId(\\d+)</code>，来确保 :userId 必须是一个数字，那么我们可以先放置详情路由。然后访问 /users/add 将不会产生冲突。这是我在 path-to-regexp 的文档中学到的技巧。</p>
<h1 id="授权路由"><a href="#授权路由" class="headerlink" title="授权路由"></a>授权路由</h1><p>在应用程序中，通常会根据用户的登录状态来限制用户访问某些路由。对于未经授权的页面（如“登录”和“忘记密码”）与已授权的页面（应用程序的主要部分）看起来不一样也是常见的。为了解决这些需求，需要考虑一个应用程序的主要入口点：<br><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">App</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>&#123;</span><br><span class="line">  render() &#123;</span><br><span class="line">    <span class="keyword">return</span> (</span><br><span class="line">      &lt;<span class="type">Provider</span> store=&#123;store&#125;&gt;</span><br><span class="line">        &lt;<span class="type">BrowserRouter</span>&gt;</span><br><span class="line">          &lt;<span class="type">Switch</span>&gt;</span><br><span class="line">            &lt;<span class="type">Route</span> path=<span class="string">"/auth"</span> component=&#123;<span class="type">UnauthorizedLayout</span>&#125; /&gt;</span><br><span class="line">            &lt;<span class="type">AuthorizedRoute</span> path=<span class="string">"/app"</span> component=&#123;<span class="type">PrimaryLayout</span>&#125; /&gt;</span><br><span class="line">          &lt;/<span class="type">Switch</span>&gt;</span><br><span class="line">        &lt;/<span class="type">BrowserRouter</span>&gt;</span><br><span class="line">      &lt;/<span class="type">Provider</span>&gt;</span><br><span class="line">    )</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>使用 react-redux 与 React Router v4 非常类似，就像之前一样，只需将 BrowserRouter包在 &lt;Provider> 中即可。</p>
<p>通过这种方法可以得到一些启发。第一个是根据我们所在的应用程序的哪个部分，在两个顶层布局之间进行选择。像访问 /auth/login 或 /auth/forgot-password 这样的路径会使用 UnauthorizedLayout —— 一个看起来适于这种情况的布局。当用户登录时，我们将确保所有路径都有一个 /app 前缀，它使用 AuthorizedRoute 来确定用户是否登录。如果用户在没有登录的情况下，尝试访问以 /app 开头的页面，那么将被重定向到登录页面。</p>
<p>虽然 AuthorizedRoute 不是 v4 的一部分，但是我在 v4 文档的帮助下自己写了。v4 中一个惊人的新功能是能够为特定的目的创建你自己的路由。它不是将 component 的属性传递给 &lt;Route>，而是传递一个 render 回调函数：<br><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">AuthorizedRoute</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>&#123;</span><br><span class="line">  componentWillMount() &#123;</span><br><span class="line">    getLoggedUser()</span><br><span class="line">  &#125;</span><br><span class="line"> </span><br><span class="line">  render() &#123;</span><br><span class="line">    const &#123; component: <span class="type">Component</span>, pending, logged, ...rest &#125; = <span class="keyword">this</span>.props</span><br><span class="line">    <span class="keyword">return</span> (</span><br><span class="line">      &lt;<span class="type">Route</span> &#123;...rest&#125; render=&#123;props =&gt; &#123;</span><br><span class="line">        <span class="keyword">if</span> (pending) <span class="keyword">return</span> &lt;div&gt;<span class="type">Loading</span>...&lt;/div&gt;</span><br><span class="line">        <span class="keyword">return</span> logged</span><br><span class="line">          ? &lt;<span class="type">Component</span> &#123;...<span class="keyword">this</span>.props&#125; /&gt;</span><br><span class="line">          : &lt;<span class="type">Redirect</span> to=<span class="string">"/auth/login"</span> /&gt;</span><br><span class="line">      &#125;&#125; /&gt;</span><br><span class="line">    )</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"> </span><br><span class="line">const stateToProps = (&#123; loggedUserState &#125;) =&gt; (&#123;</span><br><span class="line">  pending: loggedUserState.pending,</span><br><span class="line">  logged: loggedUserState.logged</span><br><span class="line">&#125;)</span><br><span class="line"> </span><br><span class="line">export <span class="keyword">default</span> connect(stateToProps)(<span class="type">AuthorizedRoute</span>)</span><br></pre></td></tr></table></figure></p>
<p>可能你的登录策略与我的不同，我是使用网络请求来 getLoggedUser()，并将 pending 和 logged 插入 Redux 的状态中。pending 仅表示在路由中请求仍在继续。</p>
<p>点击此处查看 CodePen 上完整的身份验证示例。</p>
<p><strong>其他提示</strong><br>React Router v4 还有很多其他很酷的方面。最后，一定要提几件小事，以免到时它们让你措手不及。</p>
<p><strong>&lt;Link> vs &lt;NavLink></strong><br>在 v4 中，有两种方法可以将锚标签与路由集成：&lt;Link> 和 &lt;NavLink></p>
<p>&lt;NavLink> 与 &lt;Link> 一样，但如果 &lt;NavLink> 匹配浏览器的 URL，那么它可以提供一些额外的样式能力。例如，在示例应用程序中，有一个&lt;PrimaryHeader> 组件看起来像这样：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> PrimaryHeader = <span class="function"><span class="params">()</span> =&gt;</span> (</span><br><span class="line">  &lt;header className=<span class="string">"primary-header"</span>&gt;</span><br><span class="line">    &lt;h1&gt;Welcome to our app!<span class="xml"><span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span></span><br><span class="line">    &lt;nav&gt;</span><br><span class="line">      &lt;NavLink to=<span class="string">"/app"</span> exact activeClassName=<span class="string">"active"</span>&gt;Home&lt;<span class="regexp">/NavLink&gt;</span></span><br><span class="line"><span class="regexp">      &lt;NavLink to="/</span>app/users<span class="string">" activeClassName="</span>active<span class="string">"&gt;Users&lt;/NavLink&gt;</span></span><br><span class="line"><span class="string">      &lt;NavLink to="</span>/app/products<span class="string">" activeClassName="</span>active<span class="string">"&gt;Products&lt;/NavLink&gt;</span></span><br><span class="line"><span class="string">    &lt;/nav&gt;</span></span><br><span class="line"><span class="string">  &lt;/header&gt;</span></span><br><span class="line"><span class="string">)</span></span><br></pre></td></tr></table></figure></p>
<p>使用 <navlink> 可以让我给任何一个激活的链接设置一个 active 样式。而且，需要注意的是，我也可以给它们添加 exact 属性。如果没有 exact，由于 v4 的包容性匹配策略，那么在访问 /app/users 时，主页的链接将处于激活中。就个人经历而言，NavLink 带 exact属性等价于 v3 的 &lt;link>，而且更稳定。</navlink></p>
<p><strong>URL 查询字符串</strong><br>再也无法从 React Router v4 中获取 URL 的查询字符串了。在我看来，做这个决定是因为没有关于如何处理复杂查询字符串的标准。所以，他们决定让开发者去选择如何处理查询字符串，而不是将其作为一个选项嵌入到 v4 的模块中。这是一件好事。</p>
<p>就个人而言，我使用的是 query-string，它是由 sindresorhus 大神写的。</p>
<p><strong>动态路由</strong><br>关于 v4 最好的部分之一是几乎所有的东西（包括 &lt;Route>）只是一个 React 组件。路由不再是神奇的东西了。我们可以随时随地渲染它们。想象一下，当满足某些条件时，你的应用程序的整个部分都可以路由到。当这些条件不满足时，我们可以移除路由。甚至我们可以做一些疯狂而且很酷的递归路由。</p>
<p>因为它 Just Components™，React Router 4 更简单了。</p>

      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      
        <div class="post-tags">
          
            <a href="/blog/tags/react-router/" rel="tag"># react-router</a>
          
        </div>
      

      
      
      

      
        <div class="post-nav">
          <div class="post-nav-next post-nav-item">
            
              <a href="/blog/2018/09/24/详解JS中的this、apply、call、bind/" rel="next" title="详解JS中的this、apply、call、bind">
                <i class="fa fa-chevron-left"></i> 详解JS中的this、apply、call、bind
              </a>
            
          </div>

          <span class="post-nav-divider"></span>

          <div class="post-nav-prev post-nav-item">
            
              <a href="/blog/2018/09/24/Axios的拦截器原理以及请求等待重发的实现/" rel="prev" title="Axios的拦截器原理以及请求等待重发的实现">
                Axios的拦截器原理以及请求等待重发的实现 <i class="fa fa-chevron-right"></i>
              </a>
            
          </div>
        </div>
      

      
      
    </footer>
  </div>
  
  
  
  </article>



    <div class="post-spread">
      
    </div>
  </div>


          </div>
          


          

  



        </div>
        
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside id="sidebar" class="sidebar">
    
    <div class="sidebar-inner">

      

      
        <ul class="sidebar-nav motion-element">
          <li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap">
            Table of Contents
          </li>
          <li class="sidebar-nav-overview" data-target="site-overview-wrap">
            Overview
          </li>
        </ul>
      

      <section class="site-overview-wrap sidebar-panel">
        <div class="site-overview">
          <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
            
              <p class="site-author-name" itemprop="name">lhweb</p>
              <p class="site-description motion-element" itemprop="description"></p>
          </div>

          <nav class="site-state motion-element">

            
              <div class="site-state-item site-state-posts">
              
                <a href="/blog/archives/">
              
                  <span class="site-state-item-count">39</span>
                  <span class="site-state-item-name">posts</span>
                </a>
              </div>
            

            
              
              
              <div class="site-state-item site-state-categories">
                <a href="/blog/categories/index.html">
                  <span class="site-state-item-count">6</span>
                  <span class="site-state-item-name">categories</span>
                </a>
              </div>
            

            
              
              
              <div class="site-state-item site-state-tags">
                <a href="/blog/tags/index.html">
                  <span class="site-state-item-count">25</span>
                  <span class="site-state-item-name">tags</span>
                </a>
              </div>
            

          </nav>

          
            <div class="feed-link motion-element">
              <a href="/blog/atom.xml" rel="alternate">
                <i class="fa fa-rss"></i>
                RSS
              </a>
            </div>
          

          

          
          

          
          

          

        </div>
      </section>

      
      <!--noindex-->
        <section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active">
          <div class="post-toc">

            
              
            

            
              <div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#新的-API-和新的思维模式"><span class="nav-number">1.</span> <span class="nav-text">新的 API 和新的思维模式</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#包容性路由"><span class="nav-number">2.</span> <span class="nav-text">包容性路由</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#排他性路由"><span class="nav-number">3.</span> <span class="nav-text">排他性路由</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#匹配"><span class="nav-number">4.</span> <span class="nav-text">匹配</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#授权路由"><span class="nav-number">5.</span> <span class="nav-text">授权路由</span></a></li></ol></div>
            

          </div>
        </section>
      <!--/noindex-->
      

      

    </div>
  </aside>


        
      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div class="copyright">&copy; <span itemprop="copyrightYear">2019</span>
  <span class="with-love">
    <i class="fa fa-user"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">lhweb</span>

  
</div>


  <div class="powered-by">Powered by <a class="theme-link" target="_blank" href="https://hexo.io">Hexo</a></div>



  <span class="post-meta-divider">|</span>



  <div class="theme-info">Theme &mdash; <a class="theme-link" target="_blank" href="https://github.com/iissnan/hexo-theme-next">NexT.Muse</a> v5.1.4</div>




        







        
      </div>
    </footer>

    
      <div class="back-to-top">
        <i class="fa fa-arrow-up"></i>
        
      </div>
    

    

  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>









  












  
  
    <script type="text/javascript" src="/blog/lib/jquery/index.js?v=2.1.3"></script>
  

  
  
    <script type="text/javascript" src="/blog/lib/fastclick/lib/fastclick.min.js?v=1.0.6"></script>
  

  
  
    <script type="text/javascript" src="/blog/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script>
  

  
  
    <script type="text/javascript" src="/blog/lib/velocity/velocity.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/blog/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/blog/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script>
  


  


  <script type="text/javascript" src="/blog/js/src/utils.js?v=5.1.4"></script>

  <script type="text/javascript" src="/blog/js/src/motion.js?v=5.1.4"></script>



  
  

  
  <script type="text/javascript" src="/blog/js/src/scrollspy.js?v=5.1.4"></script>
<script type="text/javascript" src="/blog/js/src/post-details.js?v=5.1.4"></script>



  


  <script type="text/javascript" src="/blog/js/src/bootstrap.js?v=5.1.4"></script>



  


  




	





  





  












  





  

  

  

  
  

  

  

  

</body>
</html>
